/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.project.imported;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.project.ImportConflictDTO;
import cn.ac.nercis.pes.model.standard.analyze.LayerEvaluationSDTO;
import cn.ac.nercis.pes.model.standard.analyze.OverallEvaluationSDTO;
import cn.ac.nercis.pes.model.standard.analyze.UnitEvaluationSDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.LayerEvaluationRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.OverallEvaluationRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.UnitEvaluationRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.model.evaluation.LayerEvaluationDO;
import cn.ac.nercis.pes.repository.model.evaluation.OverallEvaluationDO;
import cn.ac.nercis.pes.repository.model.evaluation.UnitEvaluationDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.service.utils.CheckFieldUtils;
import cn.ac.nercis.pes.service.utils.ZipUtils;
import cn.ac.nercis.pes.service.utils.DistinctUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 单元测评信息导入服务
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class UnitEvaluationImportService extends CommonImportService {
    private final UnitEvaluationRepository unitEvaluationRepository;
    private final LayerEvaluationRepository layerEvaluationRepository;
    private final OverallEvaluationRepository overallEvaluationRepository;
    private final SystemTargetRepository systemTargetRepository;

    @Autowired
    public UnitEvaluationImportService(UnitEvaluationRepository unitEvaluationRepository,
                                       LayerEvaluationRepository layerEvaluationRepository,
                                       OverallEvaluationRepository overallEvaluationRepository,
                                       SystemTargetRepository systemTargetRepository) {
        this.unitEvaluationRepository = unitEvaluationRepository;
        this.layerEvaluationRepository = layerEvaluationRepository;
        this.overallEvaluationRepository = overallEvaluationRepository;
        this.systemTargetRepository = systemTargetRepository;
    }

    public Result<List<ImportConflictDTO>> checkUnitEvaluation(@NonNull String systemId, List<UnitEvaluationSDTO> unitEvaluations,
                                                               List<LayerEvaluationSDTO> layerEvaluations,
                                                               List<OverallEvaluationSDTO> overallEvaluations) {
        List<SystemTargetDO> systemTargets = this.systemTargetRepository.findAllBySystemId(systemId);
        List<ImportConflictDTO> conflictData = checkUnitEvaluation(systemId, unitEvaluations, systemTargets);
        conflictData.addAll(checkLayerEvaluation(systemId, layerEvaluations, systemTargets));
        conflictData.addAll(checkOverallEvaluation(systemId, overallEvaluations, systemTargets));
        return Result.success(conflictData);
    }

    public Result<String> importUnitEvaluation(@NonNull String systemId, List<UnitEvaluationSDTO> unitEvaluations,
                                               List<LayerEvaluationSDTO> layerEvaluations,
                                               List<OverallEvaluationSDTO> overallEvaluations, Boolean cover) {
        Result<String> result = importUnitEvaluation(systemId, unitEvaluations.stream()
                .filter(DistinctUtils.customUnitEvaluationDistinct()).toList(), cover);
        if (!result.isSuccess()) {
            return result;
        }
        result = importLayerEvaluation(systemId, layerEvaluations.stream()
                .filter(DistinctUtils.customLayerEvaluationDistinct()).toList(), cover);
        if (!result.isSuccess()) {
            return result;
        }
        return importOverallEvaluation(systemId, overallEvaluations.stream()
                .filter(DistinctUtils.customOverallEvaluationDistinct()).toList(), cover);
    }

    private List<ImportConflictDTO> checkUnitEvaluation(String systemId, List<UnitEvaluationSDTO> unitEvaluations, List<SystemTargetDO> systemTargets) {
        List<UnitEvaluationDO> sourceData = this.unitEvaluationRepository.findAllBySystemId(systemId);
        if (CollectionUtils.isEmpty(sourceData)) {
            return Collections.emptyList();
        }
        return unitEvaluations.stream()
                .filter(DistinctUtils.customUnitEvaluationDistinct())
                .flatMap(ec -> this.checkUnitEvaluation(ec, sourceData, systemTargets).stream())
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<ImportConflictDTO> checkUnitEvaluation(@NonNull UnitEvaluationSDTO importData, List<UnitEvaluationDO> sourceData, List<SystemTargetDO> systemTargets) {
        UnitEvaluationDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getLayerTargetId(), importData.getLayerTargetId())
                        && StringUtils.equals(st.getUnitTargetId(), importData.getUnitTargetId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return Collections.emptyList();
        }
        List<ImportConflictDTO> conflictData = new ArrayList<>();
        if (ZipUtils.hasConflict(entity.getResultAnalysis(), importData.getResultAnalysis())) {
            conflictData.add(ImportConflictDTO.builder()
                    .fieldName("结果分析信息")
                    .oldContent(entity.getResultAnalysis())
                    .newContent(importData.getResultAnalysis())
                    .build());
        }
        if(CollectionUtils.isNotEmpty(conflictData)){
            String title = getTargetName(importData.getUnitTargetId(), systemTargets);
            conflictData.add(0,ImportConflictDTO.builder()
                    .fieldName(BaseConstants.IMPORT_CONFLICT_DATA_TAG)
                    .newContent(title)
                    .oldContent(title)
                    .build());
        }
        return conflictData;
    }

    private List<ImportConflictDTO> checkLayerEvaluation(String systemId, List<LayerEvaluationSDTO> layerEvaluations, List<SystemTargetDO> systemTargets) {
        List<LayerEvaluationDO> sourceData = this.layerEvaluationRepository.findAllBySystemId(systemId);
        if (CollectionUtils.isEmpty(sourceData)) {
            return Collections.emptyList();
        }
        return layerEvaluations.stream()
                .filter(DistinctUtils.customLayerEvaluationDistinct())
                .flatMap(ec -> this.checkLayerEvaluation(ec, sourceData, systemTargets).stream())
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<ImportConflictDTO> checkLayerEvaluation(@NonNull LayerEvaluationSDTO importData, List<LayerEvaluationDO> sourceData, List<SystemTargetDO> systemTargets) {
        LayerEvaluationDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getLayerTargetId(), importData.getLayerTargetId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return Collections.emptyList();
        }
        List<ImportConflictDTO> conflictData = new ArrayList<>();
        if (ZipUtils.hasConflict(entity.getResultAnalysis(), importData.getResultAnalysis())) {
            conflictData.add(ImportConflictDTO.builder()
                    .fieldName(String.format("%s(层面结果分析)", getTargetName(importData.getLayerTargetId(), systemTargets)))
                    .oldContent(entity.getResultAnalysis())
                    .newContent(importData.getResultAnalysis())
                    .build());
        }
        return conflictData;
    }

    private List<ImportConflictDTO> checkOverallEvaluation(String systemId, List<OverallEvaluationSDTO> overallEvaluations, List<SystemTargetDO> systemTargets) {
        List<OverallEvaluationDO> sourceData = this.overallEvaluationRepository.findAllBySystemId(systemId);
        if (!CollectionUtils.isEmpty(sourceData)) {
            return Collections.emptyList();
        }
        return overallEvaluations.stream()
                .filter(DistinctUtils.customOverallEvaluationDistinct())
                .flatMap(ec -> this.checkOverallEvaluation(ec, sourceData, systemTargets).stream())
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private List<ImportConflictDTO> checkOverallEvaluation(@NonNull OverallEvaluationSDTO importData, List<OverallEvaluationDO> sourceData, List<SystemTargetDO> systemTargets) {
        OverallEvaluationDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getLayerTargetId(), importData.getLayerTargetId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return Collections.emptyList();
        }
        List<ImportConflictDTO> conflictData = new ArrayList<>();
        if (ZipUtils.hasConflict(entity.getContent(), importData.getContent())) {
            conflictData.add(ImportConflictDTO.builder()
                    .fieldName(String.format("%s(层面总体评价)", getTargetName(importData.getLayerTargetId(), systemTargets)))
                    .oldContent(entity.getContent())
                    .newContent(importData.getContent())
                    .build());
        }
        return conflictData;
    }

    private Result<String> importUnitEvaluation(@NonNull String systemId, List<UnitEvaluationSDTO> unitEvaluations, Boolean cover) {
        List<UnitEvaluationDO> sourceData = this.unitEvaluationRepository.findAllBySystemId(systemId);
        Result<String> result = Result.success();
        for (UnitEvaluationSDTO item : unitEvaluations) {
            result = this.importUnitEvaluation(systemId, item, sourceData, cover);
            if (!result.isSuccess()) {
                break;
            }
        }
        return result;
    }

    private Result<String> importUnitEvaluation(String systemId, @NonNull UnitEvaluationSDTO importData, List<UnitEvaluationDO> sourceData, Boolean cover) {
        UnitEvaluationDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getLayerTargetId(), importData.getLayerTargetId())
                        && StringUtils.equals(st.getUnitTargetId(), importData.getUnitTargetId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return createUnitEvaluation(systemId, importData, sourceData);
        } else {
            return modifyUnitEvaluation(entity, importData, cover);
        }
    }

    private Result<String> createUnitEvaluation(String systemId, UnitEvaluationSDTO importData, List<UnitEvaluationDO> sourceData) {
        UnitEvaluationDO entity = UnitEvaluationDO.builder()
                .systemId(systemId)
                .layerTargetId(importData.getLayerTargetId())
                .unitTargetId(importData.getUnitTargetId())
                .resultAnalysis(importData.getResultAnalysis())
                .finishState(importData.getFinishState())
                .build();
        entity.setCreateTime(LocalDateTime.now());
        return Optional.of(entity)
                .map(this.unitEvaluationRepository::save)
                .map(data->{
                    sourceData.add(data);
                    return Result.success(data.getId());
                })
                .orElse(Result.failed("导入单元测评信息，创建失败"));
    }

    private Result<String> modifyUnitEvaluation(UnitEvaluationDO sourceData, UnitEvaluationSDTO importData, Boolean cover) {
        sourceData.setResultAnalysis(CheckFieldUtils.applyContent(sourceData.getResultAnalysis(), importData.getResultAnalysis(), cover));
        sourceData.setFinishState(CheckFieldUtils.applyContent(sourceData.getFinishState(), importData.getFinishState(), cover));
        sourceData.setModifyTime(LocalDateTime.now());
        return Optional.of(sourceData)
                .map(this.unitEvaluationRepository::save)
                .map(UnitEvaluationDO::getId)
                .map(Result::success)
                .orElse(Result.failed("导入单元测评信息，修改失败"));
    }

    private Result<String> importLayerEvaluation(@NonNull String systemId, List<LayerEvaluationSDTO> layerEvaluations, Boolean cover) {
        List<LayerEvaluationDO> sourceData = this.layerEvaluationRepository.findAllBySystemId(systemId);
        Result<String> result = Result.success();
        for (LayerEvaluationSDTO item : layerEvaluations) {
            result = this.importLayerEvaluation(systemId, item, sourceData, cover);
            if (!result.isSuccess()) {
                break;
            }
        }
        return result;
    }

    private Result<String> importLayerEvaluation(String systemId, @NonNull LayerEvaluationSDTO importData, List<LayerEvaluationDO> sourceData, Boolean cover) {
        LayerEvaluationDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getLayerTargetId(), importData.getLayerTargetId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return createLayerEvaluation(systemId, importData, sourceData);
        } else {
            return modifyLayerEvaluation(entity, importData, cover);
        }
    }

    private Result<String> createLayerEvaluation(String systemId, LayerEvaluationSDTO importData, List<LayerEvaluationDO> sourceData) {
        LayerEvaluationDO entity = LayerEvaluationDO.builder()
                .systemId(systemId)
                .layerTargetId(importData.getLayerTargetId())
                .resultAnalysis(importData.getResultAnalysis())
                .finishState(importData.getFinishState())
                .build();
        entity.setCreateTime(LocalDateTime.now());
        return Optional.of(entity)
                .map(this.layerEvaluationRepository::save)
                .map(data->{
                    sourceData.add(data);
                    return Result.success(data.getId());
                })
                .orElse(Result.failed("导入层面结果分析信息，创建失败"));
    }

    private Result<String> modifyLayerEvaluation(LayerEvaluationDO sourceData, LayerEvaluationSDTO importData, Boolean cover) {
        sourceData.setResultAnalysis(CheckFieldUtils.applyContent(sourceData.getResultAnalysis(), importData.getResultAnalysis(), cover));
        sourceData.setFinishState(CheckFieldUtils.applyContent(sourceData.getFinishState(), importData.getFinishState(), cover));
        sourceData.setModifyTime(LocalDateTime.now());
        return Optional.of(sourceData)
                .map(this.layerEvaluationRepository::save)
                .map(LayerEvaluationDO::getId)
                .map(Result::success)
                .orElse(Result.failed("导入层面结果分析信息，修改失败"));
    }

    private Result<String> importOverallEvaluation(@NonNull String systemId, List<OverallEvaluationSDTO> overallEvaluations, Boolean cover) {
        List<OverallEvaluationDO> sourceData = this.overallEvaluationRepository.findAllBySystemId(systemId);
        Result<String> result = Result.success();
        for (OverallEvaluationSDTO item : overallEvaluations) {
            result = this.importOverallEvaluation(systemId, item, sourceData, cover);
            if (!result.isSuccess()) {
                break;
            }
        }
        return result;
    }

    private Result<String> importOverallEvaluation(String systemId, @NonNull OverallEvaluationSDTO importData, List<OverallEvaluationDO> sourceData, Boolean cover) {
        OverallEvaluationDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getLayerTargetId(), importData.getLayerTargetId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return createOverallEvaluation(systemId, importData,sourceData);
        } else {
            return modifyOverallEvaluation(entity, importData, cover);
        }
    }

    private Result<String> createOverallEvaluation(String systemId, OverallEvaluationSDTO importData, List<OverallEvaluationDO> sourceData) {
        OverallEvaluationDO entity = OverallEvaluationDO.builder()
                .systemId(systemId)
                .layerTargetId(importData.getLayerTargetId())
                .content(importData.getContent())
                .finishState(importData.getFinishState())
                .build();
        entity.setCreateTime(LocalDateTime.now());
        return Optional.of(entity)
                .map(this.overallEvaluationRepository::save)
                .map(data->{
                    sourceData.add(data);
                    return Result.success(data.getId());
                })
                .orElse(Result.failed("导入层面总体评价信息，创建失败"));
    }

    private Result<String> modifyOverallEvaluation(OverallEvaluationDO sourceData, OverallEvaluationSDTO importData, Boolean cover) {
        sourceData.setContent(CheckFieldUtils.applyContent(sourceData.getContent(), importData.getContent(), cover));
        sourceData.setFinishState(CheckFieldUtils.applyContent(sourceData.getFinishState(), importData.getFinishState(), cover));
        sourceData.setModifyTime(LocalDateTime.now());
        return Optional.of(sourceData)
                .map(this.overallEvaluationRepository::save)
                .map(OverallEvaluationDO::getId)
                .map(Result::success)
                .orElse(Result.failed("导入层面总体评价信息，修改失败"));
    }

}
